/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package racer.client.gui.tiles;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import racer.client.data.RegEx;
import racer.client.data.print;
import racer.client.gui.Game;
import racer.client.gui.ImageBank;
import racer.client.gui.MapGenerator;
import racer.client.gui.entities.CarEntity;


/**
 *
 * @author Lieven
 */


//called static methods (below in this file) by mapgenerator.java
//through instance by game.java
public class TiledMap {

    private HashMap map_tileId_tileObject;
    private HashMap map_tiletype_tileTypeNeighboursObject;
    private HashMap map_tiletype_bufferedImageObject,map_direction_switchNumber;
    
    //K slaat op in welk kwadrant de bocht ligt
    //cirkel rechtsboven= 1ste kwadrant,linksboven=2,linksonder=3,rechtsonder=4
    private BufferedImage V,H,K1,K2,K3,K4,HFIN,VFIN;

    
    //x_current_tile_building_map en y_current_tile_building_map zijn coordinaten van vorige painted image
    private int x_current_tile_building_map=0;
    private int y_current_tile_building_map=0;
    private CarEntity this_player;
    private CarEntity opponent;
        //tile_id_counter om ids toe te kennen aan elke tile bij het opbouwen van de map
    private int tile_id_counter=0;
    //later ->replace by getwidth en getHeight of bufferedImage (always previous one)
    //especially needed when building map...atm only 1 tile size so np
    static final int img_tile_width=100;
    static final int img_tile_height=100;        
    
 /**
 * Class info: called through instance by game.java
 * <br>called static methods (below in this file) by mapgenerator.java
 * <p> each carEntity is set by using getters of game class.
  * <br>Images (cars/tile types) are loaded ,hashmaps are init.
  * <br>Map is built and drawn
 *
 *@param  image_g Graphics object
* @param  game game object for using its getters in TiledMap class 
 * @see   Game#getThisPlayer()
 * @see   Game#getOpponent()
 * @see   build_and_draw_Map;
 */    
    public TiledMap(Game game,Graphics image_g){
        this_player=game.getThisPlayer();
        opponent=game.getOpponent();
        loadImages_initHashMaps(game.getImageBank());
        build_and_draw_Map(image_g);
        
    }
    
 /*
  * Methods used by Game.java
  * 
  * */
 
    
 /**
 *This method will return HashMap containing all tiles of the built map (tile-id and tile objects)
 *
 * @return  HashMap 
 * @see     Tile
 */
    public HashMap getTiledMap(){
        return map_tileId_tileObject;
    }
    
   /*
      tiles uitprinten in de vorm:
      Tile ID: 1, (X,Y): (4,104), width: 100, height: 100, type: K2
      LEFT : 0
      RIGHT : 26
      TOP : 0
      BOTTOM : 2

    */
    
    //aparte printklasse
    



        /*bochten:
     * b1,boven of rechts tekenen (men komt van K2 of K4)
     * b2,boven of links tekenen (men komt van K1 of K3)
     * b3,onder of links tekenen (men komt van K2 of K4)
     * b4,onder of rechts tekenen (men komt van K1 of K3)
     *
     * onder=Y+=1
     * boven=Y-=2
     * links=X-=3
     * rechts=x+=4
     * */

    //2 coordinaten van een auto 

 /**
 *This method will init. all BufferedImages (images used to build the map: all tile types).
 *<p>Also for init. hashmaps used by TiledMap.
 * @param  ib ImageBank,responsible for loading all images
 * @see     ImageBank
 * @see     TileTypeNeighbours
 */    
    public void loadImages_initHashMaps(ImageBank ib){
           //laden van de images en verder verwerken als BufferedImage-objecten

            
           K1 = ib.getImage("b1");
           K2 = ib.getImage("b2");
           K3 = ib.getImage("b3");
           K4 = ib.getImage("b4");
           V = ib.getImage("vert");
           H = ib.getImage("hori");
           VFIN = ib.getImage("vertifin");
           HFIN = ib.getImage("horifin");

           //hier komen alle tiles in met hun id,coordinaten van hun linkerbovenhoek,
           //type tile en een buurobject die de ids van al zijn buren bijhoudt (in elke richting)
           map_tileId_tileObject=new HashMap();


           //met bits geïnitialiseerde objecten (per type tile 1 bits ingesteld
           //voor de richting die de auto kan uitgaan (afh van type tile) -> type,links,rechts,top,bottom)
           //o.a. cruciaal bij het linken van de eerste tile met de laatste op het einde

           TileTypeNeighbours ttnH = new TileTypeNeighbours("H",1,1,0,0);
           TileTypeNeighbours ttnHFIN = new TileTypeNeighbours("HFIN",1,1,0,0);
           TileTypeNeighbours ttnV = new TileTypeNeighbours("V",0,0,1,1);
           TileTypeNeighbours ttnVFIN = new TileTypeNeighbours("VFIN",0,0,1,1);
           TileTypeNeighbours ttnK1 = new TileTypeNeighbours("K1",1,0,0,1);
           TileTypeNeighbours ttnK2 = new TileTypeNeighbours("K2",0,1,0,1);
           TileTypeNeighbours ttnK3 = new TileTypeNeighbours("K3",0,1,1,0);
           TileTypeNeighbours ttnK4 = new TileTypeNeighbours("K4",1,0,1,0);
           
           map_tiletype_tileTypeNeighboursObject= new HashMap();
           map_tiletype_bufferedImageObject= new HashMap();
           map_tiletype_tileTypeNeighboursObject.put("H", ttnH);
           map_tiletype_tileTypeNeighboursObject.put("HFIN", ttnHFIN);
           map_tiletype_tileTypeNeighboursObject.put("V", ttnV);
           map_tiletype_tileTypeNeighboursObject.put("VFIN", ttnVFIN);
           map_tiletype_tileTypeNeighboursObject.put("K1", ttnK1);
           map_tiletype_tileTypeNeighboursObject.put("K2", ttnK2);
           map_tiletype_tileTypeNeighboursObject.put("K3", ttnK3);
           map_tiletype_tileTypeNeighboursObject.put("K4", ttnK4);

           //voor parsen uit txtbestand later
           map_tiletype_bufferedImageObject.put("H",H);
           map_tiletype_bufferedImageObject.put("HFIN",HFIN);
           map_tiletype_bufferedImageObject.put("VFIN",VFIN);
           map_tiletype_bufferedImageObject.put("V",V);
           map_tiletype_bufferedImageObject.put("K1",K1);
           map_tiletype_bufferedImageObject.put("K2",K2);
           map_tiletype_bufferedImageObject.put("K3",K3);
           map_tiletype_bufferedImageObject.put("K4",K4);
           
           
           //per richting een cijfer (switch case -> int)
           map_direction_switchNumber=new HashMap();
           map_direction_switchNumber.put("onder", 1);
           map_direction_switchNumber.put("boven", 2);
           map_direction_switchNumber.put("links", 3);
           map_direction_switchNumber.put("rechts", 4);
        
    }    
    
    
    
    
       /*
    Hier kan men beschrijven wat uit welke tiles de map kan bestaan
    OPGELET: VOLGORDE IS CRUCIAAL (hij gaat zelf de nieuwe positie berekenen van de tile,
    afhankelijk van de coördinaten van de oude tile én afh van de soort en de richting van de nieuwe tile.
    (ook handig voor map collision later)
    */
   

    
   /**
 *This method is used to transform the built map to a circular(chained tiles) map.
 *<br>After build_and_draw_Map-method we have a HashMap filled with tileobjects whose neighbour tiles link to each other.
 *<br>There is only 1 link missing: the link between the last and the first element to make the chain complete. 
 *<br>This method calls crossreference_2_Neighbours 2 times. One to link 1st element to last and 1 for last to link to the first. 
 *<br>This is crucial for map collision. 
 * @see     crossreference_2_Neighbours(int linkTileFromID,int linkTileToID)
 * @see     build_and_draw_Map(Graphics g)
 */    
    public void transform_to_chained_map(){
                


                //max verwijzen naar eerste
                crossreference_2_Neighbours(map_tileId_tileObject.size()-1,1);
                
               //eerste verwijzen naar laatste
                crossreference_2_Neighbours(1,map_tileId_tileObject.size()-1);                

    }

   /**
 *This method is used by transform_to_chained_map(see for more info) to transform the built map to a circular(chained tiles) map.
 *<br>The (TileType)Neighbour object is checked on fields containing integer 1 as value. <br>(This is the direction of the tile that isnt linked yet)<br>More info see: TileTypeNeighbours
 * @param     linkTileFromID tile that needs to link to tile with ID: linkTileToID
 * @param     linkTileToID tile-id that needs to be set as a neighbour tile for the tile with id linkTileFromID
 * @see       transform_to_chained_map()  
 * @see       TileTypeNeighbours     
 * @see       Tile
 */        
    public void crossreference_2_Neighbours(int linkTileFromID,int linkTileToID){
                
                Tile tile = (Tile)map_tileId_tileObject.get(linkTileFromID);
                map_tileId_tileObject.remove(linkTileFromID);
                TileTypeNeighbours neighboursTile=(TileTypeNeighbours) tile.ttn;
                //buur zoeken met 1 als id (=leeg id)

                if (neighboursTile.getBottom() == 1){
                    neighboursTile.setBottom(linkTileToID);
                    
                }else if (neighboursTile.getTop() == 1){
                    neighboursTile.setTop(linkTileToID);
                    
                }else if (neighboursTile.getLeft() == 1){
                    neighboursTile.setLeft(linkTileToID);
                    
                }else if (neighboursTile.getRight() == 1){
                    neighboursTile.setRight(linkTileToID);
                    
                }else{
                    System.err.println("can't link tile "+linkTileFromID+" to "+linkTileToID+" ! No 1 found");
                }
                

                tile.setTileTypeNeighbours(neighboursTile);
                map_tileId_tileObject.put(linkTileFromID, tile);        
                
        
    }
    
    
 /**
 *Chosing which map to build/paint by giving mapid-parameter
 *<br>Mapid 3 loads a mapfile,which was exported by MapGenerator.java <br>Later on these files will be replaced by server with database.
 * @param  g Graphics object (output to screen)        
 * @param mapid number of map to build/paint     
 * @see add_Filled_Tile_To_Map(BufferedImage bochtnummer,String type,String richting,int times,Graphics g)
 * @see MapGenerator  
 * @see    loadMapFile(String filename,Graphics g)
 */     
    
 public void choose_map(Graphics g,int mapid){
        
        switch(mapid){
            case 1:
                //start van de map
                x_current_tile_building_map=0;
                y_current_tile_building_map=0;
                //build map_tileId_tileObject and paint the tiles    
                add_Filled_Tile_To_Map(K2,"K2","onder",1,g);
                add_Filled_Tile_To_Map(V,"V","onder",2,g);
                add_Filled_Tile_To_Map(K3,"K3","onder",1,g);
                add_Filled_Tile_To_Map(H,"H","rechts",4,g);
                add_Filled_Tile_To_Map(K1,"K1","rechts",1,g);
                add_Filled_Tile_To_Map(V,"V","onder",1,g);
                add_Filled_Tile_To_Map(K3,"K3","onder",1,g);
                add_Filled_Tile_To_Map(K4,"K4","rechts",1,g);
                add_Filled_Tile_To_Map(V,"V","boven",1,g);
                add_Filled_Tile_To_Map(K2,"K2","boven",1,g);
                add_Filled_Tile_To_Map(H,"H","rechts",1,g);
                add_Filled_Tile_To_Map(K4,"K4","rechts",1,g);
                add_Filled_Tile_To_Map(V,"V","boven",1,g);
                add_Filled_Tile_To_Map(K1,"K1","boven",1,g);
                add_Filled_Tile_To_Map(H,"H","links",1,g);
                add_Filled_Tile_To_Map(HFIN,"HFIN","links",1,g);
                add_Filled_Tile_To_Map(H,"H","links",3,g);
                add_Filled_Tile_To_Map(K3,"K3","links",1,g);
                add_Filled_Tile_To_Map(K1,"K1","boven",1,g);
                add_Filled_Tile_To_Map(H,"H","links",1,g);                 
            break;
                
            case 2:
                //start van de map
                x_current_tile_building_map=0;
                y_current_tile_building_map=0;                
                //build map_tileId_tileObject and paint the tiles    
                add_Filled_Tile_To_Map(K2,"K2","onder",1,g);
                add_Filled_Tile_To_Map(V,"V","onder",2,g);
                add_Filled_Tile_To_Map(VFIN,"VFIN","onder",1,g);
                add_Filled_Tile_To_Map(K3,"K3","onder",1,g);
                add_Filled_Tile_To_Map(H,"H","rechts",8,g);
                add_Filled_Tile_To_Map(K4,"K4","rechts",1,g);
                add_Filled_Tile_To_Map(V,"V","boven",3,g);
                add_Filled_Tile_To_Map(K1,"K1","boven",1,g);
                add_Filled_Tile_To_Map(H,"H","links",8,g);                   
            break;

            case 3:

                loadMapFile("map.txt",g);

        
        //build map_tileId_tileObject and paint the tiles    
            break;            
            
            default:
            break;
        }

        
    }
    
 /**
 *This method is used to load a map from file (self-made map exported by MapGenerator.java)
 *<br>Such a file contains tiles to build (each line has fields ~ parameters of add_Filled_Tile_To_Map()-method)
  * <br>The first lines are settings such as where to start painting the map (sets global vars: x_current_tile_building_map,y_current_tile_building_map),<br>where to put the cars (coordinates of the finish line tile)
 * @param filename file to load     
 * @param  g Graphics object (output to screen)     
 * @see    CarEntity.setXY() (this_player,opponent) to put position coordinates   

 */     
    
    public void loadMapFile(String filename,Graphics g){
       String expressie="paintMapItem=(.*?),(.*?),(.*?)=";
       String expressie_start_map="Setting-startmap=(.*?),(.*?)=";
       String expressie_start_positie="Setting-startpositie=(.*?),(.*?)=";
       
       
       File mapFile = new File(filename);
    try {
      //use buffering, reading one line at a time
      //FileReader always assumes default encoding is OK!
      BufferedReader input =  new BufferedReader(new FileReader(mapFile));
      try {
        String line = null; //not declared within while loop
        /*
        * readLine is a bit quirky :
        * it returns the content of a line MINUS the newline.
        * it returns null only for the END of the stream.
        * it returns an empty String if two newlines appear in a row.
        */
        int lineteller=0;
        while (( line = input.readLine()) != null){
            lineteller++;
        if (lineteller < 3){

            Matcher vergelijk1 = RegEx.getMatcher(expressie_start_map,line);
            Matcher vergelijk2 = RegEx.getMatcher(expressie_start_positie,line);
        
            if (RegEx.Matches(vergelijk1,0)){
            
                try{

                //globale vars die het begin van de opgeroepte map voorstellen
                   x_current_tile_building_map = Integer.parseInt(vergelijk1.group(1));
                   y_current_tile_building_map = Integer.parseInt(vergelijk1.group(2));


               }catch(NumberFormatException nu){
                    System.err.println("fout"+nu);

                } 
            }else if (RegEx.Matches(vergelijk2,0)){
                try{

                
                   int car_positie_x = Integer.parseInt(vergelijk2.group(1));
                   int car_positie_y = Integer.parseInt(vergelijk2.group(2));
                   this_player.setXY((car_positie_x+30), (car_positie_y+20));
                   opponent.setXY((car_positie_x+30), (car_positie_y+55));
                    print.out(car_positie_x+","+car_positie_y);
               }catch(NumberFormatException nu){
                    System.err.println("fout"+nu);

                }                 
                
            }
        }

            Matcher vergelijk = RegEx.getMatcher(expressie,line);
        
            if (RegEx.Matches(vergelijk,0)){
                    try{
                        
                    //add_Filled_Tile_To_Map=V,V,boven,3,g=
                   //     (string apart nodig)
                   String parsed_prentje_string = vergelijk.group(1).toString();
                   BufferedImage parsed_prentje = (BufferedImage)map_tiletype_bufferedImageObject.get(parsed_prentje_string);         
                   String richting  = (vergelijk.group(2)).toString();
                   int aantalKeer = Integer.parseInt(vergelijk.group(3));
                   

                   //this.ouder.getGame().setOpponentPosition(x, y, angle,active_tile_opponent);
                   
                   System.out.println(parsed_prentje_string+","+richting+","+aantalKeer);
                   add_Filled_Tile_To_Map(parsed_prentje,parsed_prentje_string,richting,aantalKeer,g);
                    }catch(NumberFormatException nu){
                        System.err.println("fout"+nu);
                        
                    }
                   
            
            
            
            }
        }
      }
      finally {
        input.close();
        System.out.println("done");
        //System.exit(0);
      }
    }
    catch (IOException ex){
      ex.printStackTrace();
    }       
       
   }

 /**
 *This method is main method to load,build,paint map and make it circular. C "see also" for methods it calls.
 *<br>
 * @param  g Graphics object (output to screen)    
 * @see TileTypeNeighbours       
 * @see choose_map(Graphics g,int mapid)       
 * @see transform_to_chained_map()       
 */     
    
    public void build_and_draw_Map(Graphics g){ 


        //1ste argument=hoeveelste kwadrant=soort bocht of H/V (horizontale/verticale tile),HFIN=H met finishline etc
        //2de argument is type voor in tileobjecten (voor collision detection..afh van soort tile
        //3de argument is waar men de bocht moet tekenen tov vorige painted image
        //bv bocht in kwadrant 1 kan men tekenen als men van K2 komt (=rechts)
        //of kwadrant 4 (=boven) +zie onderaan voor meer uitleg
        //4de argument is het aantal keer dat je dit type tile achtereen wilt tekenen


         //als je vanuit eerste tile de previoustile opvraagt...anders teveel extra controles
         //vandaar dummy tile
        TileTypeNeighbours neighboursDummyTile = new TileTypeNeighbours("dummy",0,0,0,0);
        Tile dummytile = new Tile(0,0,0,100,100,"DUMMY",neighboursDummyTile);
        map_tileId_tileObject.put(0, dummytile);    

        //standaardmap = 1
        //choose_map(g,1);
        //choose_map(g,2);
        
        //3 is from file
        choose_map(g,3);

        
        //cross-reference first and last tile
        transform_to_chained_map();
        print.tiles(map_tileId_tileObject);


    }    
    

    
 /**
 *Concept:.f.e. paint horizontal tile type 1 time ,placed left of/from the previous tile:
  * <br>When spoken of boundaries of a tile,i mean the free boundaries (2 of the 4 possible directions)<br>Each tile gets an ID by using a tile_id_counter. This helps us loading previous tile (id--) from the tile-hashmap to set its neighbour object to the new tile (link old to new tile)<br>
  *(known because this method was called for the new tile being placed) in the direction defined by param (left).   
  * <br>Depending on the boundary direction (left)of the tile you want to add,new x,y coordinates are being calculated (to paint the new tile+save them in the tile object)
 *<br>(left top corner of the tile being placed).Because you know where new tile is being placed towards the old tile,we know the direction of the previous tile towards the new tile (being placed)(in opposite boundary direction.f.e.right in this example)
  * <br> So you can configure (set) the direction field(setRight()) of the neighbour object(link new tile to previous tile) of the new tile in this opposite direction.
  * <br>At the end,the new tile is painted (painted to a buffer so we don't have to repaint each tile,see gameLoop()).
  * <br> All of this is crucial for map collision later on.

 * @param  g Graphics object (output to screen)    
 * @param bochtnummer BufferedImage,for each tile type there is a different image to paint        
 * @param type tile type
 * @param richting position of the tile towards the previous neighbour tile (left,right,top,bottom)
 * @param times how many tiles of this type that should be added/painted
 * @see TileTypeNeighbours      
 * @see Tile      
 * @see paintFilledTile(BufferedImage soortpic,Graphics g)       
 */     
    
    public void add_Filled_Tile_To_Map(BufferedImage bochtnummer,String type,String richting,int times,Graphics g){
        //normaal onnodig om elke keer switch te testen
        //maar anders moet ik de while bij elke case zetten én nog is painten per loop
        //nodig om bits te zetten zodat men later weet welke men nog moet fixen (=1=niet ingevuld)
        TileTypeNeighbours ttns= (TileTypeNeighbours)map_tiletype_tileTypeNeighboursObject.get(type);
        int richtingInt = (Integer)map_direction_switchNumber.get(richting);
        while(times >0){
            boolean caseOK = true;
            //om nieuwe tile naar de vorige te doen wijzen
            TileTypeNeighbours neighboursnewtile = new TileTypeNeighbours(type,ttns.getLeft(),ttns.getRight(),ttns.getTop(),ttns.getBottom());
            tile_id_counter++;
            Tile newtile = new Tile((tile_id_counter),0,0,100,100,type,null);
             
            //om vorige tile naar de nieuwe te doen wijzen
            Tile previoustile = (Tile)map_tileId_tileObject.get(tile_id_counter-1);
            TileTypeNeighbours neighboursprevioustile=(TileTypeNeighbours) previoustile.getTileTypeNeighbours();
       
            
       
            switch (richtingInt){
                case 1:
                //onder
                y_current_tile_building_map+=img_tile_height;
                neighboursprevioustile.setBottom(tile_id_counter);
                //enkel als het van boven komt moet men dit controleren
                //de eerste tile wordt namelijk naar onder gezet
                //als het een tile is met een vrije richting naar boven bv verticaal ok K2/K4
                //dan gaat dit zich richten naar id=0 en dat kan dus niet
                if ((tile_id_counter-1) != 0 ){
                    neighboursnewtile.setTop(tile_id_counter-1);
                }
                break;

                case 2:
                //boven
                y_current_tile_building_map-=img_tile_height;
                neighboursprevioustile.setTop(tile_id_counter);
                neighboursnewtile.setBottom(tile_id_counter-1);                
                break;

                case 3:
                //links
                x_current_tile_building_map-=img_tile_width;
                neighboursprevioustile.setLeft(tile_id_counter);
                neighboursnewtile.setRight(tile_id_counter-1);
                break;

                case 4:
                //rechts
                x_current_tile_building_map+=img_tile_width;
                neighboursprevioustile.setRight(tile_id_counter);
                neighboursnewtile.setLeft(tile_id_counter-1);                
                break;

                default:
                System.out.println("error- wrong tile type");
                tile_id_counter--;
                caseOK=false;
                break;
            }
            
            if(caseOK){
                newtile.setXY(x_current_tile_building_map, y_current_tile_building_map);
                newtile.setTileTypeNeighbours(neighboursnewtile);
                map_tileId_tileObject.put(tile_id_counter, newtile);    

                previoustile.setTileTypeNeighbours(neighboursprevioustile);
                map_tileId_tileObject.put(tile_id_counter-1, previoustile);            
                
                //painten
                paintFilledTile(bochtnummer,g);
                //g.drawImage(bochtnummer, x_current_tile_building_map, y_current_tile_building_map, null);
            }
            times--;
        
        }


    }
    //nieuwe gedefinieerde TILE tonen op het scherm + opslaan in de hashmap (inc.buurobject)
    
 /**
 *This method fills the tyle by paiting the picture of the tile type
 *<br>It uses 2 coordinates (start position to draw) defined in add_filled_Tile_To_Map()-method :x_current_tile_building_map,y_current_tile_building_map
 * @param  g Graphics object (output to screen)    
 * @param  soortpic BufferedImage,for each tile type there is a different image to paint   
 * @see    add_Filled_Tile_To_Map(BufferedImage bochtnummer,String type,String richting,int times,Graphics g)
 */     
    
    public void paintFilledTile(BufferedImage soortpic,Graphics g){
        g.drawImage(soortpic, x_current_tile_building_map, y_current_tile_building_map, null);
       
    }    
    
    
 /*
  * STATIC ! Methods used by MapGenerator.java
  * 
  * */    
    
    
 /**
 *This method is used to load a project map,once saved by MapGenerator.Loading means building (putting the tiles in "chosenmap"-HashMap) and painting it. 
 *<br>Such a map contains tile ids and tile type (image to paint on a certain tile)
  * <br>This method also analyses the loaded map to set some booleans in the main application
  * <br>f.e. is there allready finish line?,..
 * @param mapg MapGenerator-object for using setters (set chosenmap,set finishline_present boolean)     
 * @param filename file to load     
 * @return boolean loading succeeded or failed       
 * @see MapGenerator#getChosenMap()      

 */     
    
   public static boolean loadMapInProgress(MapGenerator mapg,String filename){
       String expressie="paintMapInProgressItem=(.*?),(.*?)=";
       HashMap chosenmap = new HashMap();
       
       //needs to stay local
       boolean finishtest=false;
       File mapInProgressFile = new File(filename);
    try {
      //use buffering, reading one line at a time
      //FileReader always assumes default encoding is OK!
      BufferedReader input =  new BufferedReader(new FileReader(mapInProgressFile));
      try {
        String line = null; //not declared within while loop
        /*
        * readLine is a bit quirky :
        * it returns the content of a line MINUS the newline.
        * it returns null only for the END of the stream.
        * it returns an empty String if two newlines appear in a row.
        */
        while (( line = input.readLine()) != null){
                 
 
            Matcher vergelijk = RegEx.getMatcher(expressie,line);
        
            if (RegEx.Matches(vergelijk,0)){            
            
            
            
                    try{
                        
                    //paintMapItem=V,V,boven,3,g=
                   //     (string apart nodig)
                   int tile_id = Integer.parseInt(vergelijk.group(1));
                   String parsed_prentje_string = vergelijk.group(2).toString();
                   
                   //true zetten opdat de boolean ook ok is als men de map opslaat na het openen van een vorig project
                   //normaal checkt hij het type tile bij het tekenen/toevoegen en/of verwijderen
                   if((parsed_prentje_string.equals("HFIN"))||((parsed_prentje_string.equals("VFIN")))){
                        finishtest=true;
                   }

                   

                   //this.ouder.getGame().setTegenstanderPosition(x, y, angle,active_tile_tegenstander);
                   
                   System.out.println(parsed_prentje_string+","+tile_id);
                   //hashmap opvullen
                   chosenmap.put(tile_id,parsed_prentje_string);
                   
                    }catch(NumberFormatException nu){
                        System.err.println("fout"+nu);
                        
                    }
                   
            
            
            
            }
        } 
        
      }
      finally {
        input.close();
        System.out.println("done");
        //System.exit(0);
        if(chosenmap.size() == 0){
            return false;
        }else{
           if(finishtest){
                mapg.setfinish_line_present(true);
           }else{
                mapg.setfinish_line_present(false);
           }
           mapg.setChosenMap(chosenmap);
           return true;
        }
            
      }
    }
    catch (IOException ex){
      ex.printStackTrace();
      return false;
    }      
      
  }   
    
 /**
 *This method is used to save a project map,created by MapGenerator.Saving means (after being built/put in tile hashmap aka chosenmap) iterating HashMap and printing it to file.<br>You can load this in MapGenerator to modify it (not used as gaming map,see exportmap). 
 *<br>Such a map contains tile ids and tile type (image to paint on a certain tile)
 * @param mapg MapGenerator-object for using getter (get chosenmap)     
 * @param filename file to save
 * @return boolean saving succeeded or failed       
 * @see MapGenerator#getChosenMap()

 */          
   
   
   public static boolean saveMapInProgress(MapGenerator mapg,String filename){
     try{
   
        FileWriter fstream = new FileWriter(filename);
        BufferedWriter out = new BufferedWriter(fstream);
        HashMap chosenmap=mapg.getChosenMap();
    
         Iterator cmIterator = chosenmap.keySet().iterator();
        while(cmIterator.hasNext()) {
            int mid = (Integer)cmIterator.next();
            String type = (String)chosenmap.get(mid);
            System.out.println("saving mapInProgress tile ID:"+mid+", type"+type+"=");
            out.write("paintMapInProgressItem="+mid+","+type+"=");
            out.newLine();
  
        }
    
    //Close the output stream
    out.close();
    return true;
    }catch (Exception e){//Catch exception if any
      System.err.println("Error: " + e.getMessage());
      return false;
    }
     
      
  }  
    
 /**
 *This method is used to export a project map,created by MapGenerator.Exporting means (after being built/put in tile hashmap aka chosenmap) iterating HashMap and printing it to file. 
 *<br>This exported format is the format being used by our game application (where one can choose to load a map from file)
 *<br>Such a file contains tile-info to build in the game (each line has fields ~ parameters of add_Filled_Tile_To_Map()-method)
 * <br>The first lines are settings such as where to start painting the map (sets global vars: x_current_tile_building_map,y_current_tile_building_map),<br>where to put the cars (coordinates of the finish line tile)
  *<p><b>IMPORTANT</b> Loading a map in the game application means we have to analyse the constructed map and check if the maker respected the ground rules. f.e. 
  * <br>Is the map circular (1st tile and last are chained?No other tiles outside the chain),is there min&&max 1 finish line present? Other rules (which tiles can coexist,..) are checked while placing tiles (before exporting)
 * @param mapg MapGenerator-object for using getters/setters (setting booleans so when method returns we know what ground rule got broken)
 * @return boolean exporting succeeded or failed       
 * @see MapGenerator#getChosenMap()
 * @see MapGenerator#getTileMap()
 * @see MapGenerator#getTypetilemap()
 * @see MapGenerator#gettileIDdistanceBetweenDirectionsMap()
 * @see MapGenerator#setallElementsUsedInChainedMap(boolean allElementsUsedInChainedMap)
 * @see MapGenerator#setisMapCircular(boolean isMapCircular);
* @see    add_Filled_Tile_To_Map(BufferedImage bochtnummer,String type,String richting,int times,Graphics g)
 * @see MapGenerator

 */    
   
  public static boolean exportMap(MapGenerator mapg){
      HashMap chosenmap=mapg.getChosenMap();
      HashMap tile_map=mapg.getTileMap();
      HashMap typetilemap=mapg.getTypetilemap();
      HashMap tileIDdistanceBetweenDirectionsMap = mapg.gettileIDdistanceBetweenDirectionsMap();
      //HashMap checkAllElementsmap=mapg.getChosenMap();
      ArrayList checkAllElementsmap = new ArrayList();
      //resetting when earlier tried to export
      mapg.setallElementsUsedInChainedMap(true);
      //analyseMapBeforeSaving
        int mid=1;
        //willekeurig id bepalen die in de chosenmap zit en vandaar vertrekt het script
         Iterator cm2Iterator = chosenmap.keySet().iterator();
        if(cm2Iterator.hasNext()) {
            mid = (Integer)cm2Iterator.next();      
        }
         
            //begincoordinaat van map meegeven
            Tile tiler = (Tile)tile_map.get(mid);
            int startmapMINX = tiler.x;
            int startmapMINY=tiler.y;
                      
        String FileString="";
        
        String type = (String)chosenmap.get(mid);
        //eerste element onder zette
       FileString+="paintMapItem="+type+",onder,1=\n";
        System.out.println("Tile Type:"+type+", richting:onder ,aantal keer:"+1);    
        //bij eerste element/tile zal hij eerste boolean op zen pad nemen
        boolean left=true;
        boolean right=true;
        boolean top=true;
        boolean bottom=true;
        //startpositie...als het toevallig niet zo gevonden worden later..is het de positie van de eerste tile
        //LATER: y in 100 want in game.jave wordt de eerste tile naar onder gezet = +100 in de y richting
        int finishline_y=tiler.y;
        int finishline_x=tiler.x;

        int tileteller=1;
        //richting
        String previous="";
        String prevprev="rechts";
        //String thPrevious="";
        //voor de eerste keer
        String previous_type="onder";
        //String next_tile_placement="rechts";
        int prev_type_teller = 1;
        //<= voor de controle of de laatste gelinkt is aan de eerste
        //maar op einde laatste regel niet meer uitprinten naar de file(bestaat al)
        //print.chosentiles(chosenmap);
        while(tileteller <= (chosenmap.size()+1)){
            tileteller++;
            
            //print.out(""+mid);
            
            
            //script hieronder zal vanaf een tile beginnen zoeken naar zijn gevulde buren en zo een chain maken
            //wanneer er een volgende mid niet bestaat..dwz dat er een gat is in de chain
            //maar er moet ook controle komen over het aantal gebruikte elementen in de chain 
            //want je kan een chained tilevorm maken maar als er nog elders een vakje ingevuld staat....
            //vandaar de checkAllElementsmap
            
            if (!chosenmap.containsKey(mid)){
             
             mapg.setisMapCircular(false);
             System.out.println("map niet circulair");
             return false;   
            }else{
             
                //allready used
                if(!checkAllElementsmap.contains(mid)){
                    checkAllElementsmap.add(mid);
                }
                }
                    
            
            //zowieso al 1 finishline vooraleer men deze methode oproept (controle wanneer men wil saven
            //enkel nog coördinaat zoeken
            if((type.equals("HFIN"))||(type.equals("VFIN"))){
                finishline_x = ((Tile)tile_map.get(mid)).x;
                finishline_y= ((Tile)tile_map.get(mid)).y;
            }
            
            switch (Integer.parseInt(typetilemap.get(type).toString())){

            //steeds een x en y waarde vergelijken van de tile met positie van de auto (overschreden? => collision)
            //als de tegenovergestelde x en y waarde overschreden is : nieuwe tile

             //x en y elk een if/else geven (nooit buurrichtingen in 1 2delige-if/else structuur)
            //geen onlogische waarschuwing aangezien men 2 richtingen per collision hebt en 2 richtingen om actieve tile te triggeren
            case 1:
                //H
                
                if(left){
                    
                    right=false;

                    top=true;
                    bottom=true;                    
                    
                    previous="links";
                    
                }else if(right){
                    previous="rechts";
                    top=true;
                    bottom=true;                                                           
                    left=false;
                }

            
            break;
            
            case 2:
                //V
                
                if(bottom){
                    previous="onder";
                    top=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                }
  
            break;
            
            case 3:
                //K1
                
                if(bottom){
                    previous="onder";
                    top=false;
                    right=true;
                    left=true;
                }else if(left){
                    previous="links";
                    top=true;
                    bottom=true;                                        
                    right=false;
                }                
           
            break;
            
            case 4:
                //K2
                
                if(right){
                    top=true;
                    bottom=true;                                        
                    previous="rechts";
                    left=false;                    
                }else if(bottom){
                    previous="onder";
                    top=false;                    
                    right=true;
                    left=true;
                }                

                
            //bepaal de 2 rijrichtingen
            //rechts of onder

        
            break;
            
            case 5:
                //K3
                
                if(right){
                    top=true;
                    bottom=true;                                        
                    previous="rechts";
                    left=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                    right=true;
                    left=true;                    
                }                
            break;
            
            case 6:
                //K4
                if(left){
                    top=true;
                    bottom=true;                                        
                    previous="links";
                    right=false;
                }else if(top){
                    previous="boven";
                    bottom=false;
                    right=true;
                    left=true;                    
                }                
            break;            
                
                default:
                    break;
            }
            mid+=(Integer)tileIDdistanceBetweenDirectionsMap.get(previous);
            previous_type=type;
            type = (String)chosenmap.get(mid);            
            if(tileteller <= chosenmap.size()){
            FileString+="paintMapItem="+type+","+previous+",1=\n";    
            System.out.println("Tile Type:"+type+", richting: "+previous+" ,aantal keer:"+prev_type_teller);    
                }
                
                


            

            
        
        }
        
        if(checkAllElementsmap.size() != chosenmap.size()){
             mapg.setallElementsUsedInChainedMap(false);
             System.out.println("not all ids are used ");
             return false;    
        }
        
        //test of elk element voorkomt in de chain
        

            int start_mapX= startmapMINX;
            int start_mapY= startmapMINY;

            String filename="map.txt";
             try{
            // Create file 
            FileWriter fstream = new FileWriter(filename);
            BufferedWriter out = new BufferedWriter(fstream);
            
            out.write("Setting-startmap="+start_mapX+","+(start_mapY-100)+"=\n");
            out.write("Setting-startpositie="+finishline_x+","+finishline_y+"=\n");
            out.write(FileString);
            //Close the output stream
            out.close();
            return true;
            }catch (Exception e){//Catch exception if any
              System.err.println("Error: " + e.getMessage());
              return false;
            }
    //        System.out.println("saving mapInProgress tile ID:"+mid+", type"+type+"=");



      
      
  }  
  
 /**
 *This method is used to create/build an empty grid/rastered map. A HashMap is being filled with all tiles(including its coordinates in the field,calculated by rows/cols/tile_height/tile_width)<br>possible in the dimension formed by the amount of columns/rows of the map. 
 *<br>The tile boundaries are painted in black(combined with drawRasteredMap) and the HashMap is passed to MapGenerator object.
 * @param mapg MapGenerator-object for using setters (setting chosenmap null and setting tile_map that just got filled)
 * @param cols amount of columns to draw
 * @param rows amount of rows to draw
 * @param tile_width
 * @param tile_height
 *@see drawRasteredMap(Graphics g,int cols,int rows,int tile_width,int tile_height)
 * @see MapGenerator#setChosenMap(HashMap)
 * @see MapGenerator#setTileMap(HashMap)
 * @see MapGenerator

 */    
  
  public static void buildRasteredMap(MapGenerator mapg,int cols,int rows,int tile_width,int tile_height){
   
       HashMap tile_map = new HashMap();          
       int tileid=0;
          for(int j =0;j<(rows*tile_height);j+=tile_height){
            for(int i =0;i<(cols*tile_width);i+=tile_width){

                 tileid++;
                 TileTypeNeighbours ttnteep=new TileTypeNeighbours(tileid+"",0,0,0,0);
                 Tile newtile = new Tile(tileid,i,j,100,100,"empty",ttnteep);
                 tile_map.put(tileid, newtile);                        

            }
          }
       mapg.setTileMap(tile_map);
       //this will be filled when peope fill a tile
       mapg.setChosenMap(new HashMap());
  }
    
  
  
  //PAINT METHODS
 /**
 *This method will paint tile-id of the active tile (where the entity(car) is driving) 
 *(only in Debug-mode)  
 *
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  tileidstr the text to print/paint
 */
  public static void paintActiveTileNumber(Graphics g,int x,int y,int w,int h,String tileidstr){
            g.setColor(Color.BLACK);
            g.drawString(tileidstr, (x+(w/8)), (y+(h/5)));
  }
 
 /**
 *This method will paint a rectangle at the left top corner(always) of the car image (where the entity(car) is driving) 
 *(only in Debug-mode). It uses paintCollisionRectangle()-method.  
 *
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  c color
  * @see paintCollisionRectangle(Graphics g,int x,int y,int w,int h,Color c)
 */   
  
 public static void paintImagePositionCoordinate(Graphics g,int x,int y,int w,int h,Color c){
      paintCollisionRectangle(g,x,y,w,h,c);
 }
/**
 *This method will paint a rectangle at the given position. Here its the collisionpoint of the car image (depending on driving direction and angle of the car) 
 *(only in Debug-mode). It uses fillrect-method.
 *
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param  c color
 */   

 public static void paintCollisionRectangle(Graphics g,int x,int y,int w,int h,Color c){
            g.setColor(c);
            g.fillRect(x,y,w+5,h+5);  
 }    
 /**
 *This method is used to paint an empty grid/rastered map.(combined with buildRasteredMap) 
 *<br>The tile boundaries are painted in black.
 * @param g Graphics object (output to screen)  
 * @param cols amount of columns to draw
 * @param rows amount of rows to draw
 * @param tile_width
 * @param tile_height
 * @see buildRasteredMap(MapGenerator mapg,int cols,int rows,int tile_width,int tile_height)
 * @see MapGenerator

 */    

  public static void drawRasteredMap(Graphics g,int cols,int rows,int tile_width,int tile_height){
             
          g.setColor(Color.BLACK);
          for(int j =0;j<(rows*tile_height);j+=tile_height){
            for(int i =0;i<(cols*tile_width);i+=tile_width){           
                g.drawRect(i,j,100,100);          
                }
            }            
    }  
/**
 *This method will paint rectangle boundary lines at the given position of the active_tile.
 *(in Debug-mode if used in game application,normal mode if used in MapGenerator). It uses drawrect-method.
 *
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 */   

  public static void paintActiveTileBounds(Graphics g,int x,int y,int w,int h){
            g.setColor(Color.RED);
            g.drawRect(x,y,w,h);       
  }
  

  /**
 *This method will paint a chosen tile type (picture) at the given position of the active_tile.
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 * @param image2draw BufferedImage to paint (tile_type)
 */   

      
  public static void paintFillActiveTile(Graphics g,int x,int y,int w,int h,BufferedImage image2draw){
        g.drawImage(image2draw, x, y, null);
  }
  
  /**
 *This method will paint a filled red rectangle (filled with WRONG TILE text) at the given position of the active_tile. It means that <br>when the map creator/user puts down a chosen tile,an error occured (not allowed tile type on that position)
 * @param  g Graphics object (output to screen) 
 * @param  x the location (position x-coordinate) where to start painting
 * @param  y the location (position y-coordinate) where to start painting
 * @param  w width of a tile,used to get a good position for the text,nothing more
 * @param  h height of a tile,used to get a good position for the text,nothing more
 */      

  public static void paintTileError(Graphics g,int x,int y,int w,int h){
  
            g.setColor(Color.RED);
            g.fillRect(x, y, w, h);  
            g.setColor(Color.BLACK);
            g.drawString("WRONG TILE",(x+5),(y+(h/2)));            
    }   
  
  
}
